<?php


namespace app\api\qqpay;

class QQPay
{
	const API_URL_PREFIX = "https://qpay.qq.com";
	const UNIFIEDORDER_URL = "/cgi-bin/pay/qpay_unified_order.cgi";
	const ORDERQUERY_URL = "/cgi-bin/pay/qpay_order_query.cgi";
	const CLOSEORDER_URL = "/cgi-bin/pay/qpay_close_order.cgi";
	private $appid;
	private $mch_id;
	private $nonce_str;
	private $sign;
	private $body;
	private $out_trade_no;
	private $fee_type;
	private $total_fee;
	private $spbill_create_ip;
	private $notify_url;
	private $trade_type;
	private $key;
	public $postCharset = "UTF-8";
	public $signType = "HMAC-SHA1";
	private $SSLCERT_PATH;
	private $SSLKEY_PATH;
	private $params = [];
	private $logs;
	private $fileCharset = "UTF-8";
	private $appkey;
	public function __construct($qqArr)
	{
		$this->mch_id = $qqArr["mch_id"];
		$this->notify_url = $qqArr["notify_url"];
		$this->key = $qqArr["key"];
		$this->spbill_create_ip = $qqArr["ip"];
		$this->fee_type = "CNY";
	}
	public function unifiedOrder($params)
	{
		$this->params["mch_id"] = $this->mch_id;
		$this->params["nonce_str"] = $this->nonce_str = $this->genRandomString();
		$this->params["body"] = $this->body = $params["body"];
		$this->params["out_trade_no"] = $this->out_trade_no = $params["out_trade_no"];
		$this->params["fee_type"] = $this->fee_type;
		$this->params["total_fee"] = $this->total_fee = $params["total_fee"] * 100;
		$this->params["spbill_create_ip"] = $this->spbill_create_ip;
		$this->params["notify_url"] = $this->notify_url;
		$this->params["trade_type"] = $this->trade_type = $params["trade_type"];
		$this->sign = $this->MakeSign($this->params);
		$this->params["sign"] = $this->sign;
		$xml = $this->data_to_xml($this->params);
		$response = $this->postXmlCurl($xml, self::API_URL_PREFIX . self::UNIFIEDORDER_URL);
		$result = $this->xml_to_data($response);
		if (!empty($result["result_code"]) && !empty($result["err_code"])) {
			$result["err_msg"] = $this->error_code($result["err_code"]);
		}
		return $result;
	}
	public function orderQuery($out_trade_no)
	{
		$this->params["mch_id"] = $this->mch_id;
		$this->params["nonce_str"] = $this->genRandomString();
		$this->params["out_trade_no"] = $out_trade_no;
		$this->sign = $this->MakeSign($this->params);
		$this->params["sign"] = $this->sign;
		$xml = $this->data_to_xml($this->params);
		$response = $this->postXmlCurl($xml, self::API_URL_PREFIX . self::ORDERQUERY_URL);
		$result = $this->xml_to_data($response);
		if (!empty($result["result_code"]) && !empty($result["err_code"])) {
			$result["err_msg"] = $this->error_code($result["err_code"]);
		}
		return $result;
	}
	public function closeOrder($out_trade_no)
	{
		$this->params["mch_id"] = $this->mch_id;
		$this->params["nonce_str"] = $this->genRandomString();
		$this->params["out_trade_no"] = $out_trade_no;
		$this->sign = $this->MakeSign($this->params);
		$this->params["sign"] = $this->sign;
		$xml = $this->data_to_xml($this->params);
		$response = $this->postXmlCurl($xml, self::API_URL_PREFIX . self::CLOSEORDER_URL);
		$result = $this->xml_to_data($response);
		return $result;
	}
	public function sdkExecute($request)
	{
		$this->setupCharsets($request);
		$params["appid"] = $request["appid"];
		$params["bargainorId"] = $request["bargainorId"];
		$params["tokenId"] = $request["tokenId"];
		$params["nonce"] = $request["nonce"];
		$params["timestamp"] = time();
		$params["sign"] = $request["sign"];
		$params["sigType"] = $this->signType;
		$params["completion"] = $this->notify_url;
		foreach ($params as &$value) {
			$value = $this->character($value, $params["charset"]);
		}
		return http_build_query($params);
	}
	protected function checkEmpty($value)
	{
		if (!isset($value)) {
			return true;
		}
		if ($value === null) {
			return true;
		}
		if (trim($value) === "") {
			return true;
		}
		return false;
	}
	public function getAppPayParams($prepayid)
	{
		$ls_arr = ["appId" => $this->appid, "bargainorId" => $this->mch_id, "nonce" => $this->genRandomString(), "pubAcc" => "", "tokenId" => $prepayid];
		$sign = $this->appMakeSign($ls_arr, $this->appkey);
		$ls_arr["sig"] = $sign;
		$ls_arr["timeStamp"] = time();
		$ls_arr["sigType"] = "HMAC-SHA1";
		return $ls_arr;
	}
	private function character($data, $targetCharset)
	{
		if (!empty($data)) {
			$fileType = $this->fileCharset;
			if (strcasecmp($fileType, $targetCharset) != 0) {
				$data = mb_convert_encoding($data, $targetCharset, $fileType);
			}
		}
		return $data;
	}
	private function setupCharsets($request)
	{
		if ($this->checkEmpty($this->postCharset)) {
			$this->postCharset = "UTF-8";
		}
		$str = preg_match("/[\\x80-\\xff]/", $this->appId) ? $this->appId : print_r($request, true);
		$this->fileCharset = mb_detect_encoding($str, "UTF-8, GBK") == "UTF-8" ? "UTF-8" : "GBK";
	}
	public function getNotifyData()
	{
		$xml = $GLOBALS["HTTP_RAW_POST_DATA"];
		$data = [];
		if (empty($xml)) {
			return false;
		}
		$data = $this->xml_to_data($xml);
		if (!empty($data["return_code"])) {
			if ($data["return_code"] == "FAIL") {
				return false;
			}
		}
		return $data;
	}
	public function replyNotify()
	{
		$data["return_code"] = "SUCCESS";
		$data["return_msg"] = "OK";
		$xml = $this->data_to_xml($data);
		echo $xml;
	}
	public function MakeSign($params)
	{
		ksort($params);
		$string = $this->ToUrlParams($params);
		$string = $string . "&key=" . $this->key;
		$string = md5($string);
		$result = strtoupper($string);
		return $result;
	}
	public function ToUrlParams($params)
	{
		$string = "";
		if (!empty($params)) {
			$array = [];
			foreach ($params as $key => $value) {
				if ($key != "sign") {
					$array[] = $key . "=" . $value;
				}
			}
			$string = implode("&", $array);
		}
		return $string;
	}
	public function appMakeSign($params, $appkey)
	{
		ksort($params);
		$string = $this->ToUrlParams($params);
		$appkey = $appkey . "&";
		$result = base64_encode(hash_hmac("sha1", $string, $appkey, true));
		return $result;
	}
	public function data_to_xml($params)
	{
		if (!is_array($params) || count($params) <= 0) {
			return false;
		}
		$xml = "<xml>";
		foreach ($params as $key => $val) {
			if (is_numeric($val)) {
				$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
			} else {
				$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
			}
		}
		$xml .= "</xml>";
		return $xml;
	}
	public function xml_to_data($xml)
	{
		if (!$xml) {
			return false;
		}
		$data = json_decode(json_encode(simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA)), true);
		return $data;
	}
	public function validationSignature($params)
	{
		ksort($params);
		$string = $this->ToUrlParams($params);
		return $string;
		$string = $string . "&key=" . $this->key;
		$string = md5($string);
		$result = strtoupper($string);
		return $result;
	}
	private static function getMillisecond()
	{
		$time = explode(" ", microtime());
		$time = $time[1] . $time[0] * 1000;
		$time2 = explode(".", $time);
		$time = $time2[0];
		return $time;
	}
	private function genRandomString($len = 32)
	{
		$chars = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
		$charsLen = count($chars) - 1;
		shuffle($chars);
		$output = "";
		for ($i = 0; $i < $len; $i++) {
			$output .= $chars[mt_rand(0, $charsLen)];
		}
		return $output;
	}
	private function postXmlCurl($xml, $url, $useCert = false, $second = 30)
	{
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		if ($useCert == true) {
			curl_setopt($ch, CURLOPT_SSLCERTTYPE, "PEM");
			curl_setopt($ch, CURLOPT_SSLKEYTYPE, "PEM");
		}
		curl_setopt($ch, CURLOPT_POST, true);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		$data = curl_exec($ch);
		if ($data) {
			curl_close($ch);
			return $data;
		} else {
			$error = curl_errno($ch);
			curl_close($ch);
			return false;
		}
	}
	public function error_code($code)
	{
		$errList = ["LACK_PARAMS" => "缺少必要的请求参数", "NOAUTH" => "商户未开通此接口权限", "NOTENOUGH" => "用户帐号余额不足", "ORDERNOTEXIST" => "订单号不存在", "ORDERPAID" => "商户订单已支付，无需重复操作", "ORDERCLOSED" => "当前订单已关闭，无法支付", "SYSTEMERROR" => "系统错误!系统超时", "APPID_NOT_EXIST" => "参数中缺少APPID", "MCHID_NOT_EXIST" => "参数中缺少MCHID", "APPID_MCHID_NOT_MATCH" => "appid和mch_id不匹配", "OUT_TRADE_NO_USED" => "同一笔交易不能多次提交", "SIGNERROR" => "参数签名结果不正确", "XML_FORMAT_ERROR" => "XML格式错误", "REQUIRE_POST_METHOD" => "未使用post传递参数 ", "POST_DATA_EMPTY" => "post数据不能为空", "NOT_UTF8" => "未使用指定编码格式"];
		if (array_key_exists($code, $errList)) {
			return $errList[$code];
		}
	}
}